﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using System.Diagnostics;

namespace Syfuhs.APIWrappers.LiveMeeting
{
    static class TypeParser
    {
        public static dynamic ParseOption(string element, string value, string name)
        {
            return Parse(value, GetOptionType(element, name));
        }

        public static dynamic Parse(string value, Type t)
        {
            if (t.IsEnum)
                return ParseEnum(value, t);

            return Convert.ChangeType(value, t, CultureInfo.InvariantCulture);
        }

        private static dynamic ParseEnum(string value, Type t)
        {
            try
            {
                return Enum.Parse(t, value, true);
            }
            catch
            {
                // certain enum's start with numbers from the XML...can't compile that
                // so an X was added
                // Considered underscore, but CLS doesn't like that
                return Enum.Parse(t, "X" + value, true);
            }
        }

        private static Type GetEnumType(string name)
        {
            return Type.GetType(typeof(TypeParser).Namespace + "." + name.CapitalizeFirstCharacter());
        }

        private static Type GetOptionType(string element, string name)
        {
            switch (element)
            {
                case "ApplicationSharingOption":
                    return typeof(ApplicationSharingOption);

                case "BooleanOption":
                    return typeof(bool);

                case "DecimalOption":
                    return typeof(double);

                //case "EmptyOption":
                //    return typeof();

                case "EnumerationOption":
                    return GetEnumType(name);

                case "LocaleOption":
                    return typeof(ICustomFormatter);

                case "LocaleListOption":
                    return typeof(List<ICustomFormatter>);

                case "PrintingOption":
                    return typeof(PrintingOption);

                case "String": // same thing
                case "StringOption":
                    return typeof(string);

                case "StringListOption":
                    return typeof(List<string>);

                case "TimeOption":
                    return typeof(DateTime);

                default:
                    return typeof(string);
            }
        }

        // This starts getting tricky if we need to parse a node into a collection, and any of those nodes have collections attached
        // Need to recurse through the nodes and properties

        // Currently this implementation only goes one level deep :-(
        // Need to write out recursive logic... ugh

        public static object ParseXmlNodes(object s, XmlNodeList optionList)
        {
            foreach (XmlNode options in optionList)
                foreach (XmlNode node in options)
                    if (!node.HasChildNodes || IsEnum(node))
                        SetSingle(s, node);
                    else
                        SetCollection(s, node);
            return s;
        }

        private static bool IsEnum(XmlNode node)
        {
            return GetOptionType(node.Name, node.Attributes["name"].Value.CapitalizeFirstCharacter()).BaseType == typeof(Enum);
        }

        private static void SetSingle(object s, XmlNode node)
        {
            var prop = s.GetType().GetProperty(node.Attributes["name"].Value.CapitalizeFirstCharacter());

            if (prop == null) // Didn't need to capitalize
                prop = s.GetType().GetProperty(node.Attributes["name"].Value);

            if (prop == null) // Property doesn't seem to exist
            {
                SetUnknownProperty(s, node);
                Debug.WriteLine("public " + node.Name + " " + node.Attributes["name"].Value + "{get;set;}");
            }
            else
            {
                prop.SetValue(s, TypeParser.ParseOption(node.Name, node.Attributes["value"].Value, node.Attributes["name"].Value), null);
            }
        }

        private static void SetUnknownProperty(object s, XmlNode node)
        {
            KeyValuePair<string, string> unknown = new KeyValuePair<string, string>(node.Attributes["name"].Value, node.Attributes["value"].Value);

            List<KeyValuePair<string, string>> origProp = (List<KeyValuePair<string, string>>)s.GetType().BaseType.GetProperty("UnTypedProperties").GetValue(s, null);

            origProp.Add(unknown);
        }

        private static void SetCollection(object s, XmlNode node)
        {
            // TODO: need to go by type, not just use a string
            List<string> coll = new List<string>();

            // HACK: need to find a better way to pull out custom structures
            if (node.Name.Equals("LocaleListOption"))
                ParseLocales(s, node);
            else if (node.Name.Equals("LocaleOption"))
                ParseLocale(s, node);
            else
            {
                foreach (XmlNode child in node.ChildNodes)
                    coll.Add(child.InnerText);

                var prop = s.GetType().GetProperty(node.Attributes["name"].Value.CapitalizeFirstCharacter());

                if (prop == null)
                    prop = s.GetType().GetProperty(node.Attributes["name"].Value);

                prop.SetValue(s, coll, null);
            }
        }

        private static void ParseLocales(object s, XmlNode node)
        {
            List<CultureInfo> cultures = new List<CultureInfo>();

            foreach (XmlNode child in node.ChildNodes)
                cultures.Add(new CultureInfo(child.Attributes["language"].Value + "-" + child.Attributes["country"].Value));

            var prop = s.GetType().GetProperty(node.Attributes["name"].Value.CapitalizeFirstCharacter());

            if (prop == null)
                prop = s.GetType().GetProperty(node.Attributes["name"].Value);

            prop.SetValue(s, cultures, null);
        }

        private static void ParseLocale(object s, XmlNode node)
        {
            foreach (XmlNode child in node.ChildNodes)
            {
                var prop = s.GetType().GetProperty(node.Attributes["name"].Value.CapitalizeFirstCharacter());

                if (prop == null)
                    prop = s.GetType().GetProperty(node.Attributes["name"].Value);

                prop.SetValue(s, new CultureInfo(child.Attributes["language"].Value + "-" + child.Attributes["country"].Value), null);
            }
        }
    }
}
